package example.wikipedia;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import feign.gson.GsonDecoder;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;

public class WikipediaExample {

    static ResponseAdapter<Page> pagesAdapter = new ResponseAdapter<Page>() {

        @Override
        protected String query() {
            return "pages";
        }

        @Override
        protected Page build(JsonReader reader) throws IOException {
            Page page = new Page();
            while (reader.hasNext()) {
                String key = reader.nextName();
                if (key.equals("pageid")) {
                    page.id = reader.nextLong();
                } else
                    if (key.equals("title")) {
                        page.title = reader.nextString();
                    } else {
                        reader.skipValue();
                    }
            }
            return page;
        }
    };

    public static void main(String... args) throws InterruptedException {
        Gson gson = new GsonBuilder().registerTypeAdapter(new TypeToken<Response<Page>>() {
        }.getType(), pagesAdapter).create();

        Wikipedia wikipedia = Feign.builder().decoder(new GsonDecoder(gson)).logger(new Logger.ErrorLogger()).logLevel(Logger.Level.BASIC).options(new Request.Options(10, TimeUnit.SECONDS, 60, TimeUnit.SECONDS, true)).target(Wikipedia.class, "https://en.wikipedia.org");

        System.out.println("Let's search for PTAL!");
        Iterator<Page> pages = lazySearch(wikipedia, "PTAL");
        while (pages.hasNext()) {
            System.out.println(pages.next().title);
        }
    }

    /**
     * this will lazily continue searches, making new http calls as necessary.
     *
     * @param wikipedia used to search
     * @param query     see {@link Wikipedia#search(String)}.
     */
    static Iterator<Page> lazySearch(final Wikipedia wikipedia, final String query) {
        final Response<Page> first = wikipedia.search(query);
        if (first.nextOffset == null) {
            return first.iterator();
        }
        return new Iterator<Page>() {
            Iterator<Page> current = first.iterator();
            Long nextOffset = first.nextOffset;

            @Override
            public boolean hasNext() {
                while (!current.hasNext() && nextOffset != null) {
                    System.out.println("Wow.. even more results than " + nextOffset);
                    Response<Page> nextPage = wikipedia.resumeSearch(query, nextOffset);
                    current = nextPage.iterator();
                    nextOffset = nextPage.nextOffset;
                }
                return current.hasNext();
            }

            @Override
            public Page next() {
                return current.next();
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }

    public static interface Wikipedia {

        @RequestLine("GET /w/api.php?action=query&continue=&generator=search&prop=info&format=json&gsrsearch={search}")
        Response<Page> search(@Param("search") String search);

        @RequestLine("GET /w/api.php?action=query&continue=&generator=search&prop=info&format=json&gsrsearch={search}&gsroffset={offset}")
        Response<Page> resumeSearch(@Param("search") String search, @Param("offset") long offset);
    }

    static class Page {

        long id;
        String title;
    }

    public static class Response<X> extends ArrayList<X> {

        /**
         * when present, the position to resume the list.
         */
        Long nextOffset;
    }
}
